package com.sun.common.aop;

import com.alibaba.fastjson.JSON;
import com.sun.net.httpserver.HttpContext;
import com.sun.utils.HttpContextUtils;
import com.sun.utils.IpUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.net.IPv6Utils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Objects;

@Component
@Aspect
/*
 * @Aspect:是aspectj框架中的注解。
 *   作用：表示当前类是切面类
 *   切面类：用来给业务方法增加功能的类，在这个类中有切面功能的代码。
 *   位置：在类定义的上面
 */
@Slf4j
public class LogAspect {

    /**
     * @Pointcut:定义和管理切入点，如果你对项目中有多个切入点表达式，是重复的，
     * 可以复用的，可以使用Pointcut
     *  属性：value 切入点表达式
     *  位置：在自定义的方法的上面
     *  特点：
     *      当使用@Pointcut定义在一个方法的上面，此时这个方法的名称就是切入点表达式
     *      的别名，其它的直接中，value的属性就可以使用这个方法的名称，作为切入点表达式。
     */
    @Pointcut("@annotation(com.sun.common.aop.LogAnnotation)")
    public void pt(){}

     /*
   环绕通知方法定义格式
    1、public
    2、必须得有一个返回值，推荐使用Object
    3、方法名称自定义
    4、方法有参数，是固定的参数 ProceedingJointPoint
    */

    /*
    环绕通知
        @Around
        属性：value 切入点表达式
        位置:在方法定义的上面
    特点：
        1、它是功能最强的通知
        2、它可以在目标方法的前后加入增强功能
        3、能控制目标方法是否执行
        4、它能够修改原来目标方法是执行结果，影响最后的调用结果
    环绕通知等同于JDK的动态代理，InvocationHandler接口！！！

    参数：ProceedingJoinPoint joinPoint就等同于Method
        作用：执行目标方法
        返回值：目标方法的执行结果，这个结果可以修改

    环绕通知：经常做事务，在目标方法之前开启事务，执行目标方法，在目标方法执行结束后，提交事务。
     */
    @Around("pt()")
    public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        //执行方法
        Object proceed = joinPoint.proceed();
        //执行时长
        long l = System.currentTimeMillis() - startTime;
        //保存日志
        recordLog(joinPoint,startTime);
        return proceed;
    }

    private void recordLog(ProceedingJoinPoint joinPoint,long time){
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        LogAnnotation annotation = method.getAnnotation(LogAnnotation.class);
        log.info("==========================log start==========================");
        log.info("module:{}",annotation.module());
        log.info("operation:{}",annotation.operator());

        //请求方法名
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = signature.getName();
        log.info("request method:{}",className+"."+methodName+"()");

        //请求参数
        Object[] args = joinPoint.getArgs();
        String string = JSON.toJSONString(args);
        log.info("params:{}",string);

        //获取request 设置IP地址
        HttpServletRequest httpServletRequest = HttpContextUtils.getHttpServletRequest();
        log.info("ip:{}", IpUtils.getIpAddr(httpServletRequest));

        log.info("excute time : {} ms",time);
        log.info("=====================================log end====================================");
    }

}


